package openGL11;

import OBJ_Loader.Material;
import graphic.Buffers;
import graphic.Color;
import graphic.Sun;
import graphic.TextureBase;
import input.Input;
import objects.Triangle;
import java.nio.FloatBuffer;
import objects.SRect;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.Texture;

/**
 *  
 * @author Marcin & Bartek
 * 
 * @description It defines all methods for service the openGl engine
 */
public class Engine {
    int width;
    int height;
    Color black=new Color(0,0,0,1);
    Color white=new Color(1,1,1,1);
    public void clear(){
        try{
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public int getWidth(){ return width; }
    public int getHeight(){ return height; }
    public void startDrawT(){
        GL11.glBegin(GL11.GL_TRIANGLES);
    }
    public void drawTT(Triangle t, int d){
        GL11.glNormal3f(t.normal[0], t.normal[1], t.normal[2]);
        GL11.glTexCoord2f(1, 1);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glTexCoord2f(1, 0);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glTexCoord2f(0, 1);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);  
    }
   
    public void setColor(Color c){
        GL11.glColor4f(c.R,c.G,c.B,c.A);
    }
    
    public void drawTTrans(Triangle t,float x,float y, float z){
        GL11.glNormal3f(t.normal[0], t.normal[1], t.normal[2]);
        GL11.glVertex3f(t.x[0]+x, t.y[0]+y, t.z[0]+z);
        GL11.glVertex3f(t.x[1]+x, t.y[1]+y, t.z[1]+z);
        GL11.glVertex3f(t.x[2]+x, t.y[2]+y, t.z[2]+z);  
    }
    
    public void drawT(Triangle t){
        GL11.glNormal3f(t.normal[0], t.normal[1], t.normal[2]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);  
    }
    
    public void drawTN(Triangle t){
        GL11.glNormal3f(t.normalx[0], t.normaly[0], t.normalx[0]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glNormal3f(t.normalx[1], t.normaly[1], t.normaly[1]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glNormal3f(t.normalx[2], t.normaly[2], t.normalz[2]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);   
    }
    public void drawTNn(Triangle t){
        GL11.glNormal3f(t.normal[0], t.normal[1], t.normal[2]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);  
    }
    
    public void drawTNwT(Triangle t){
        GL11.glNormal3f(t.normalx[0], t.normaly[0], t.normalx[0]);
        GL11.glTexCoord2f(t.texx[0], t.texz[0]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glNormal3f(t.normalx[1], t.normaly[1], t.normaly[1]);
        GL11.glTexCoord2f(t.texx[1], t.texz[1]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glNormal3f(t.normalx[2], t.normaly[2], t.normalz[2]);
        GL11.glTexCoord2f(t.texx[2], t.texz[2]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);   
    }
    public void drawTNswT(Triangle t){
        GL11.glNormal3f(t.normal[0], t.normal[1], t.normal[2]);
        GL11.glTexCoord2f(t.texx[0], t.texz[0]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glTexCoord2f(t.texx[1], t.texz[1]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glTexCoord2f(t.texx[2], t.texz[2]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);   
    }
    
    public void textCoord(Vector2f v){
        GL11.glTexCoord2f(v.x, v.y);
    }
    
    
    public void bind(Texture tex){
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex.getTextureID());
    }
    public void unbind(){
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public void drawTW(Triangle t){
        GL11.glColor3f(1f, 1f, 1f);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);        
    }
    public void drawTG(Triangle t){
        GL11.glColor3f(0f, 1f, 0f);
        
        GL11.glNormal3f(t.normalx[0], t.normaly[0], t.normalx[0]);
        GL11.glVertex3f(t.x[0], t.y[0], t.z[0]);
        GL11.glNormal3f(t.normalx[1], t.normaly[1], t.normaly[1]);
        GL11.glVertex3f(t.x[1], t.y[1], t.z[1]);
        GL11.glNormal3f(t.normalx[2], t.normaly[2], t.normalz[2]);
        GL11.glVertex3f(t.x[2], t.y[2], t.z[2]);        
    }
    
    public void endDraw(){
        GL11.glEnd();
    }
    
    public void startList(int nr){
        GL11.glNewList(nr, GL11.GL_COMPILE);
    }
    public void endList(){
        GL11.glEndList();
    }
    
    public int createList(){
        return GL11.glGenLists(1);
    }
    
    public void initLight(){
        GL11.glEnable(GL11.GL_LIGHTING);
        GL11.glEnable(GL11.GL_LIGHT0);
        
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, Buffers.floatBuffer(new float[]{0.3f,0.3f,0.3f,1}));
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, Buffers.floatBuffer(new float[]{0.9f,0.8f,0.8f,0.8f}));
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, Buffers.floatBuffer(new float[]{0.5f,0.5f,0.5f,1}));
        GL11.glLight(GL11.GL_LIGHT0,GL11.GL_POSITION,Buffers.floatBuffer(new float[]{0f,1f,0f,1f}));
        
        GL11.glEnable(GL11.GL_COLOR_MATERIAL);
        
        GL11.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_DIFFUSE);
        
    }
    public void fogInit(){
        GL11.glEnable( GL11.GL_FOG );
        
        GL11.glHint( GL11.GL_FOG_HINT, GL11.GL_NICEST );
       
        GL11.glFogi( GL11.GL_FOG_MODE, GL11.GL_LINEAR );
        
        GL11.glFog(GL11.GL_FOG_COLOR, Buffers.floatBuffer(new float[]{0.9f,0.9f,0.9f,1f}) );
        GL11.glFogf( GL11.GL_FOG_DENSITY, 0.2f );
    
        GL11.glFogf( GL11.GL_FOG_START, 100.f );
        GL11.glFogf( GL11.GL_FOG_END, 200.f );
    }
    public void setClearColor(Color c){
        GL11.glClearColor(c.R, c.G, c.B, c.A);
    }
    
    public void start2D(){
        
        GL11.glDisable(GL11.GL_LIGHTING);
        GL11.glViewport(0,0,width,height);
        
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glOrtho(0, width, height, 0, 1, -1);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        
        Input.reversed=true;
    }
    public void init3D(){
        
       
        GL11.glShadeModel(GL11.GL_SMOOTH);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glEnable(GL11.GL_NORMALIZE);
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);  
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        initLight();
        setDefMtl();
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        //GL11.glEnable(GL11.GL_CULL_FACE);
        
        GL11.glEnable(GL11.GL_ALPHA);
        GL11.glEnable(GL11.GL_BLEND);
       
        fogInit();
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GLU.gluPerspective( 40.f, (float)width/(float)height,0.1f, 2000.f);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        
        Input.reversed=false;
    }
    public void start3D(){
        GL11.glEnable(GL11.GL_LIGHTING);
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GLU.gluPerspective( 40.f, (float)width/(float)height,0.1f, 200.f);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);        
    }
    
    public Engine(int width, int height){
        this.width=width;
        this.height=height;
        init3D();  
    }
    public void reverseCull(){
        GL11.glFrontFace(GL11.GL_CW);
    }
    public void normalCull(){
        GL11.glFrontFace(GL11.GL_CCW);
    }
    public void dualCull(){
        GL11.glDisable(GL11.GL_CULL_FACE);
    }
    
    public void setCam(float x,float z,float nx,float ny,float nz,float ty){
        GL11.glLoadIdentity();
        GLU.gluLookAt(x+nx, ny+ty, z+nz,  /* eye is at (0,0,5) */
            x, 0.f+ty, z,      /* center is at (0,0,0) */
            -nx, ny, -nz);      /* up is in positive Y direction */ 
        Sun.forceRefresh();
    }
    
    public void setSun(float degree){
        float xSun=(float)Math.sin(degree);
        float ySun=(float)Math.cos(degree); 
        
        GL11.glLight(GL11.GL_LIGHT0,GL11.GL_POSITION,Buffers.floatBuffer(new float[]{xSun,ySun,0.2f,0.0f}));
    }

    public void setNormal(Vector3f v) {
        GL11.glNormal3f(v.x, v.y, v.z);
    }
    
    public void drawVertex(Vector3f v) {
        GL11.glVertex3f(v.x, v.y, v.z);
    }
    public void setDefMtl(){
        GL11.glMaterial( GL11.GL_FRONT, GL11.GL_AMBIENT, Buffers.floatBuffer(new float[]{0.5f,0.5f,0.5f,1}));
        GL11.glMaterial( GL11.GL_FRONT, GL11.GL_DIFFUSE, Buffers.floatBuffer(new float[]{1f,1f,1f,1}) );
        //GL11.glMaterial( GL11.GL_FRONT, GL11.GL_SPECULAR, Buffers.floatBuffer(new float[]{0.7f,0.7f,0.7f,1}) );
        //GL11.glMaterialf( GL11.GL_FRONT, GL11.GL_SHININESS, 1000f );
    }
    public void useMtl(Material m){
        GL11.glMaterial( GL11.GL_FRONT, GL11.GL_AMBIENT, Buffers.floatBuffer(new float[]{m.getAmb().x,m.getAmb().y,m.getAmb().z, m.getD()}));
        GL11.glMaterial( GL11.GL_FRONT, GL11.GL_DIFFUSE, Buffers.floatBuffer(new float[]{m.getDiff().x,m.getDiff().y,m.getDiff().z, m.getD()}) );
        GL11.glMaterial( GL11.GL_FRONT, GL11.GL_SPECULAR, Buffers.floatBuffer(new float[]{m.getSpec().x,m.getSpec().y,m.getSpec().z, m.getD()}) );
        GL11.glMaterialf( GL11.GL_FRONT, GL11.GL_SHININESS, m.getNs());
    }
    public void drawList(int ID) {
        GL11.glCallList(ID);
    }

    public void push() {
        GL11.glPushMatrix();
    }

    public void translate(Vector3f pos) {
        GL11.glTranslatef(pos.x, pos.y, pos.z);
    }
    
    public void translate3f(float x, float y, float z) {
        GL11.glTranslatef(x, y, z);
    }

    public void pop() {
        GL11.glPopMatrix();
    }

    public void scale(float scale) {
        GL11.glScalef(scale, scale, scale);
    }
    
    public void scale(float scaleX,float scaleY,float scaleZ) {
        GL11.glScalef(scaleX, scaleY, scaleZ);
    }

    public void rotX(float x) {
        GL11.glRotatef(x, 1f, 0f, 0f);
    }

    public void rotY(float y) {
        GL11.glRotatef(y, 0f, 1f, 0f);
    }

    public void rotZ(float z) {
        GL11.glRotatef(z, 0f, 0f, 1f);
    }
    
    public void rot(float x,float y,float z){
        GL11.glRotatef(1, x, y, z);
    } 
    public void drawRect(SRect r){
        GL11.glLineWidth(5f);
        GL11.glBegin(GL11.GL_LINE_LOOP);
            GL11.glVertex3d(r.x1, 1f, r.y1);
            GL11.glVertex3d(r.x1, 1f, r.y2);
            GL11.glVertex3d(r.x2, 1f, r.y2);
            GL11.glVertex3d(r.x2, 1f, r.y1);
        GL11.glEnd();
    }

    public void bindNr(int texID) {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
    }

    public void draw2dRect(SRect r,boolean back) { 
        GL11.glLineWidth(2f);
        GL11.glBegin(GL11.GL_LINE_LOOP);
            GL11.glVertex2d(r.x1, r.y1);
            GL11.glVertex2d(r.x1, r.y2);
            GL11.glVertex2d(r.x2, r.y2);
            GL11.glVertex2d(r.x2, r.y1);
        GL11.glEnd();
        if(back){
            setColor(black);
            GL11.glBegin(GL11.GL_QUADS);
                GL11.glVertex3d(r.x1, r.y1,0.1f);
                GL11.glVertex3d(r.x1, r.y2,0.1f);
                GL11.glVertex3d(r.x2, r.y2,0.1f);
                GL11.glVertex3d(r.x2, r.y1,0.1f);
            GL11.glEnd();
            
        }
    }
    public void drawSprite(SRect r,Texture tex) { 
            setColor(white);
            bind(tex);
            GL11.glBegin(GL11.GL_QUADS);
                GL11.glTexCoord2f(0, 0);
                GL11.glVertex3d(r.x1, r.y1,0.1f);
                GL11.glTexCoord2f(0, 1);
                GL11.glVertex3d(r.x1, r.y2,0.1f);
                GL11.glTexCoord2f(1, 1);
                GL11.glVertex3d(r.x2, r.y2,0.1f);
                GL11.glTexCoord2f(1, 0);
                GL11.glVertex3d(r.x2, r.y1,0.1f);
            GL11.glEnd();
            unbind();
    }
}
